package com.xh.easyretry.common;

import com.xh.easyretry.annotations.XHRetry;
import com.xh.easyretry.visitor.RetryVisitor;
import com.xh.easyretry.visitor.Visitor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;

public class XHRetryContext implements Element {

    private Integer currentRetryCount;
    private Class<?> beanClass;
    private Object bean;
    private Method method;
    private Object[] args;
    private XHRetry xhRetry;
    private Visitor visitor;
    private XHTimeAvoidanceContext timeAvoidanceContext;

    public XHRetryContext() {
    }

    public XHRetryContext(Class<?> beanClass, Object bean, Method method, Object[] args) {
        this(XHRetryConstants.DEFAULT_START_RETRY_COUNT, beanClass, bean, method, args);
    }

    public XHRetryContext(Integer currentRetryCount, Class<?> beanClass, Object bean, Method method, Object[] args) {
        this.currentRetryCount = Optional.ofNullable(currentRetryCount).orElse(XHRetryConstants.DEFAULT_START_RETRY_COUNT);
        this.beanClass = beanClass;
        this.bean = bean;
        this.method = method;
        this.args = args;
        this.xhRetry = method.getAnnotation(XHRetry.class);
    }

    public XHRetryContext(Integer currentRetryCount, Class<?> beanClass, Object bean, Method method, Object[] args, XHRetry xhRetry, Visitor visitor) {
        this.currentRetryCount = currentRetryCount;
        this.beanClass = beanClass;
        this.bean = bean;
        this.method = method;
        this.args = args;
        this.xhRetry = xhRetry;
        this.visitor = visitor;
    }

    public Integer getCurrentRetryCount() {
        return currentRetryCount;
    }

    public void setCurrentRetryCount(Integer currentRetryCount) {
        this.currentRetryCount = currentRetryCount;
    }

    public Class<?> getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public XHRetry getXhRetry() {
        return xhRetry;
    }

    public void setXhRetry(XHRetry xhRetry) {
        this.xhRetry = xhRetry;
    }

    public Visitor getVisitor() {
        return visitor;
    }

    public void setVisitor(Visitor visitor) {
        this.visitor = visitor;
    }

    public XHTimeAvoidanceContext getTimeAvoidanceContext() {
        return timeAvoidanceContext;
    }

    public void setTimeAvoidanceContext(XHTimeAvoidanceContext timeAvoidanceContext) {
        this.timeAvoidanceContext = timeAvoidanceContext;
    }

    @Override
    public String toString() {
        return "XHRetryContext{" +
                "currentRetryCount=" + currentRetryCount +
                ", beanClass=" + beanClass +
                ", bean=" + bean +
                ", method=" + method +
                ", args=" + Arrays.toString(args) +
                ", xhRetry=" + xhRetry +
                ", visitor=" + visitor +
                ", timeAvoidanceContext=" + timeAvoidanceContext +
                '}';
    }

    @Override
    public void accept() {
        visitor.visit(this);
    }

    @Override
    public void accept(Visitor visitor) {
        this.visitor = visitor;
        accept();
    }

    public static class XHRetryContextBuilder {

        private final XHRetryContext retryContext;
        private final ProceedingJoinPoint pjp;

        public XHRetryContextBuilder(ProceedingJoinPoint pjp) {
            retryContext = new XHRetryContext();
            this.pjp = pjp;
        }

        public XHRetryContextBuilder buildCurrentRetryCount() {
            retryContext.setCurrentRetryCount(XHRetryConstants.DEFAULT_START_RETRY_COUNT);
            return this;
        }

        public XHRetryContextBuilder buildBeanClass() {
            retryContext.setBeanClass(pjp.getSignature().getDeclaringType());
            return this;
        }

        public XHRetryContextBuilder buildBean() {
            retryContext.setBean(pjp.getTarget());
            return this;
        }

        public XHRetryContextBuilder buildMethod() {
            retryContext.setMethod(((MethodSignature) pjp.getSignature()).getMethod());
            return this;
        }

        public XHRetryContextBuilder buildArgs() {
            retryContext.setArgs(pjp.getArgs());
            return this;
        }

        public XHRetryContextBuilder buildXHRetry() {
            retryContext.setXhRetry(((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(XHRetry.class));
            return this;
        }

        public XHRetryContextBuilder buildVisitor() {
            retryContext.setVisitor(RetryVisitor.getInstance());
            return this;
        }

        public XHRetryContext build() {
            return retryContext;
        }
    }
}
